// PersonInitializationBeforeUse.aj
//
// Informatics 102 Spring 2012
// Code Example
//
// This aspect weaves code into the Person class that requires a Person's
// name to have been initialized before other methods can be called on it.

public aspect PersonInitializationBeforeUse
{
	// This is called an "inter-type declaration."  What we're doing is
	// adding a field to the Person class.  It's important that we do
	// this.  We can't make the field part of the aspect, because we
	// need a separate isInitialized value for every Person.  So we'll
	// inject the field into the class from the outside.
	//
	// Note that when we say that the field is private, we mean that
	// it's private to the aspect.  All Persons have this field, but
	// even Persons don't know it; only the aspect is allowed to
	// access it.
	//
	// We'll set this up to be initialized to false automatically for
	// a newly-created Person.
	private boolean Person.isInitialized = false;
	

	// This pointcut matches calls to the setName() method in the
	// Person class.  The advice we're going to write is going to
	// need to know what Person is having its name set, so we hoist
	// that information out of the affected code by using the
	// primitive pointcut "this."
	//
	// Recall that "this," in a Java method, is a reference to the
	// "current object" (i.e., the object on which the current method
	// was called).  In a pointcut, "this" means "At every join point
	// at which this pointcut applies, copy the value of 'this,' in
	// that context, into the pointcut parameter p.  That value will
	// then be carried into any advice that uses this pointcut.
	pointcut initializationOfPerson(Person p):
		execution(void Person.setName(String)) && this(p);
	

	// This is a kind of advice we haven't seen yet, called "around
	// advice."  Whereas before advice weaves code before a join
	// point and after advice weaves code after a join point, around
	// advice completely replaces a join point.  In this case, we're
	// replacing the body of the setName() method with a new body.
	//
	// Many times in around advice, you want to (at least conditionally)
	// run the code that you're replacing.  In this case, for example,
	// it's not that we don't want to do what setName() used to do;
	// we just want to be sure to set isInitialized to true once the
	// name has been set.  The way you say "Run the code that this
	// advice replaces" is to call a pseudo-method called "proceed,"
	// passing it parameters that mirror the parameters passed to
	// the advice.  Those parameters will be carried back into the
	// replaced code, which will then execute.
	//
	// Note that around advice has a return type (in this case, void),
	// unlike before or after advice.  That's because around advice
	// may be replacing code that returns a value (such as the body
	// of a method, a call to a method, or code that fetches the value
	// of a field).
	void around(Person p):
		initializationOfPerson(p)
	{
		proceed(p);
		p.isInitialized = true;
	}
	
	
	// Note, also, that we could have written this advice as after
	// advice, though we'd want to be a little bit careful.  The Person
	// is only initialized if setName() was successful, so we'd have to
	// say this:
	//
	// after(Person p) returning:
	//     initializationOfPerson(p)
	// {
	//     p.isInitialized = true;
	// }
	//
	// where "returning" means that we don't want the advice to apply
	// if the code being advised threw an exception.  There is also
	// "after throwing" advice, which looks like this:
	//
	// after(Person p) throwing (NullPointerException e):
	//    initializationOfPerson(p)
	// {
	//      ...
	// }
	//
	// This would apply only in the case that the code being advised
	// threw a NullPointerException.
	

	
	// Now we want to recognize methods that require initialization
	// checking.  In this case, that's the getName() and toString()
	// methods.  We also need to know what Person the methods are
	// being called on.  So we'll use the primitive pointcut "target"
	// to hoist that information out of the code being advised and
	// into the pointcut parameter p; that information can then be
	// passed into the advice.
	//
	// Note that "target" and "this" seem fairly similar, but they're
	// not the same:
	//
	// * The "this" pointcut means to hoist the value of the "this"
	//   reference from the code at the join point.  When combined
	//   with a "call" pointcut, that will be the object that is
	//   calling the method.  When combined with an "execution"
	//   pointcut, that will be the object on which the method is
	//   executing.
	// * The "target" pointcut means to hoist the value of the
	//   target of the operation in question.  When combined with a
	//   "call" pointcut, that will be the object on which the method
	//   is being called.  When combined with an "execution," that
	//   will be the object on which the method is executing.
	pointcut methodsThatRequireInitialization(Person p):
		(call(String Person.getName()) || call(String Person.toString()))
		&& target(p);


	// This advice does the checking for initialization.  It's much
	// like similar advice we wrote in the Lineup example previously.
	before(Person p):
		methodsThatRequireInitialization(p)
	{
		if (!p.isInitialized)
		{
			throw new UninitializedObjectException();
		}
	}
}
